package com.sd365.gateway.authentication.service.impl;

import cn.hutool.http.HttpStatus;
import com.alibaba.fastjson.JSONObject;
import com.sd365.common.util.TokenUtil;
import com.sd365.gateway.authentication.service.AuthenticationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
 * @Class AuthenticationServiceImpl
 * @Description abel.zhan 2022-10-12 重构认证代码
 * @Author Administrator
 * @Date 2022-10-12  20:08
 * @version 1.0.0
 */
@Slf4j
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
    /**
     * 定义了认证接口的URL
     */
    public static final   String AUTH_URL = "http://sd365-permission-center/permission/centre/v1/user/auth?code=%s&account=%s&password=%s";
    /**
     * RedisTemplateConfig 中生成
     */
    @Resource(name = "tokenRedisTemplate")
    private RedisTemplate redisTemplate;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * CurrentServiceTemplateBeanConfig.java中定义
     */
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 默认设置三天过期 单位毫秒 1000 * 60 * 60 * 24 * 3 = 86400
     */
    @Value("${jwt.period}")
    private Long period = 259200000L;
    /**
     * 一天
     */
    private static final Long ONE_HOUR = 86400000L;
    private static final String USER_TOKEN_KEY = "user:token";

    /***
     * @Description 做用户登录验证， 返回值携带加密后的token
     *
     * @Param
     * @return java.lang.String
     * @Date 2022/12/20 20:51
     * @Author hantianbing
     **/
    @Override
    public String doAuthentication(String code, String password, String account) {
        //获取request
        HttpServletRequest request = getRequest();
        // 由于restTemplate请求会导致header丢失，所以手动添加header
        HttpHeaders headers = new HttpHeaders();
        headers.set(HttpHeaders.USER_AGENT, request.getHeader("USER-AGENT"));

        HttpEntity entity = new HttpEntity<>(null, headers);

        // 用户中心返回的CommonResponse 包含 UserVO对象
        LinkedHashMap UserMessageMap = null;
        try {
            UserMessageMap = restTemplate.exchange(String.format(AUTH_URL, code, account, password), HttpMethod.GET, entity, LinkedHashMap.class).getBody();
        } catch (RestClientException e) {
            e.printStackTrace();
        }
        if(UserMessageMap == null){
            throw new RuntimeException("UserMessageMap 是空的");
        }
        //返回结果携带加密后的token
        return getJWTToken(code, account, UserMessageMap);
    }

    /**
     * @Description: 生成具体的JWT token生成的算法
     * @Author: Administrator
     * @DATE: 2022-10-12  20:14
     * @param: 账号密码 以及用户的租户机构公司等信息
     * @return: 生成的JWT TOKEN
     */
    public String getJWTToken(String code, String account, LinkedHashMap authResult) {
        //获取body
        Object bodyTemp = authResult.get("body");
        if(Objects.isNull(bodyTemp)){
            throw new RuntimeException("body为空");
        }
        final LinkedHashMap body = (LinkedHashMap) bodyTemp;

        // 获取body中的code， 如果是错误码， 就直接返回
        Integer businessCode=(Integer)body.get("code");
        // 如果认证没通过就先返回 authResult{head:{code,message},body:{userVO:{code,message,data:{token } roleids:[]}}}
        if (businessCode != HttpStatus.HTTP_OK) {
            return new JSONObject(authResult).toJSONString();
        }

        //获取header
        JSONObject header = getHeader();
        //获取playLoad
        JSONObject playLoad = getPlayLoad(body, code, account);
        //拼接header和playLoad
        String jwtProtocol = header.toJSONString() + "." + playLoad.toJSONString();
        //加密token
        String encoderToken = TokenUtil.encoderToken(jwtProtocol);
        //token存入返回结果
        ((LinkedHashMap) (body).get("data")).put("token", encoderToken);
        //获取userId
        Long userId = Long.parseLong((String) body.getOrDefault("id", "-1"));
        //存入redis
        storeInRedis(userId, encoderToken);
        // 返回的报文带了JWT规范加密后的token
        return new JSONObject(authResult).toJSONString();
    }

    /**
     *
     * @param userId
     * @param jwtProtocol
     */
    private void storeInRedis(Long userId, String encoderToken){
        //redis中存储用户id
        redisTemplate.opsForValue().set(userId.toString(), "userId:" + userId.toString(), 1L, TimeUnit.DAYS);
        // 设置redis中token时间比实际时间多一个小时
        stringRedisTemplate.opsForValue().set(USER_TOKEN_KEY + userId, encoderToken , period + ONE_HOUR, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取header
     * @return
     */
    private JSONObject getHeader(){
        final JSONObject header = new JSONObject();
        header.put("alg", "HS256");
        header.put("typ", "JWT");
        return header;
    }

    /**
     * 传入用户信息获取playLoad
     * @param body
     * @return
     */
    private JSONObject getPlayLoad(LinkedHashMap body, String code, String account){
        //获取数据信息
        ArrayList roleIds = (ArrayList) (body).get("roleIds");
        Long userId = Long.parseLong((String) body.getOrDefault("id", "-1"));
        Long tenantId = Long.parseLong((String) body.getOrDefault("tenantId", "-1"));
        Long companyId = Long.parseLong((String) body.getOrDefault("companyId", "-1"));
        Long orgId = Long.parseLong((String) body.getOrDefault("orgId", "-1"));
        String userName = (String) body.getOrDefault("name", "-1");

        //设置playLoad
        final JSONObject playLoad = new JSONObject();
        playLoad.put("account", account);
        playLoad.put("roleIds", roleIds);
        playLoad.put("tenantId", tenantId);
        playLoad.put("companyId", companyId);
        playLoad.put("orgId", orgId);
        playLoad.put("userId", userId);
        playLoad.put("userName", userName);
        playLoad.put("code", code);
        // 设置token 3天过期
        playLoad.put("expiresAt",new Date(System.currentTimeMillis() + period));
        return playLoad;
    }


    /***
     * @Description 通过body里的信息， 获取header + playLoad
     *              的拼接的token， 未加密
     * @Param
     * @return java.lang.String
     * @Date 2022/12/17 16:53
     * @Author hantianbing
     **/
    private String creatToken(LinkedHashMap body,String code, String account ){

        return null;
    }

    /***
     * @Description 获取HTTPrequest
     *
     * @Param
     * @return javax.servlet.http.HttpServletRequest
     * @Date 2022/12/17 16:40
     * @Author hantianbing
     **/
    private HttpServletRequest getRequest(){
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = attributes.getRequest();
        log.debug("HttpServletRequest request ====>{}", attributes.getRequest());
        if(request == null){
            throw new RuntimeException("request是空的");
        }
        return request;
    }
}
